import React from 'react';
import './index.css';
import { Button } from 'antd'
import {
  observable, action, computed,
  makeObservable, autorun,
  configure, runInAction,
  when, reaction
} from 'mobx'
import { observer, inject } from 'mobx-react';
// import './ES6/decorator'

/**
 * 注意:
 *     多页面状态
 *     异步同步
 * /

/**
* configure()配置mobx严格模式
* 定义必须通过action的方法修改状态
* 但是
* 事实:这里配了严格模式不会警告,不配严格模式才警告,而且还可以成功修改和监听到
* 我的理解:配严格模式的时候警告且不能修改状态，不配的时候不警告啊啊啊啊啊啊啊
* */
configure({
  enforceActions: 'observed '
})

// 1、初始化 mobx 容器仓库
class Store {
  constructor() {
    //mobx 6.0 要加这个
    makeObservable(this)
  }
  /**
   * observable()
   * 可以给变量添加监听,和vue声明在data属性的变量类似
   * */
  @observable
  count1 = 0
  @observable
  count2 = 0

  /**
   * action()
   * 用来定义行为(方法)
   * 通过action 修改变量，observer()监听变量改变，然后再渲染 
  */
  @action
  increment() {
    this.count1 = this.count1 + 2
  }
  @action
  incrementcount2() {
    this.count2 = this.count2 + 2
  }
  // 状态机
  @action.bound
  setStoreValue(key, value) {
    this[key] = value
  }
  /**
   *  action.bound()
   *  为了保证方法里的this永远指向Store容器
   *  action() 可能会出现this指向调用者或者undefined 的情况
  */
  // 置零函数 
  @action.bound
  resetCout() {
    this.count1 = this.count2 = 0
  }
  /** 
   * computed()
   * 可以定义在store里面，也可以定义在class 里面
   * 依赖的变量(observable声明的)发生该变就执行一下
   * 刚进入页面时触发一次（即使依赖两个变量）
   * 有缓存功能，多次调用，只是拿到上次的值
   * */
  //  计算属性sum1 
  @computed get sumStore() {
    return this.count1 + this.count2
  }
  /**
   *  action 异步的情况
   *  这里给出一个错误的方法和三个正确的方法
  */
  @action.bound
  asyncChange() {
    /**
     * “错误方法”（注意有双引号）
     * 直接在异步里面修改状态,老是说不行，但是事实是可以的
    */
    setTimeout(() => { this.count1 = 99 }, 500)
    /**
      * 正确方法1:
      *  在异步里面再调用一个action方法修改状态
      *  例如：在异步里调用setStoreValue()
     */
    setTimeout(() => { this.setStoreValue('count1', 999) }, 1000)
    /**
     * 正确方法2:
     * 和第一个方法原理一样类似，在异步定义一个立即执行的action函数
     * 例如:立即执行函数:ChanValuLiMa
     * 
    */
    setTimeout(() => {
      action('ChanValuLiMa', () => {
        this.count1 = 9999
      })()
    }, 1500)
    /**
     * 正确方法3:
     * 使用runInAction
    */
    setTimeout(() => {
      runInAction(() => { this.count1 = 99999 })
    }, 2000)
  }

  /**
   *  突然有一个想法，runInaction 是同步的还是异步的 ？
   *  我可以在Store 的不是action修饰的函数中返回一个runInaction（）吗？
   * 
   * */
  returnRunInaction() {
    return runInAction(() => {
      this.count1 = 888
      return 'data'
    })
  }




}





const store = new Store()

/** autorun()
 *  监测的变量发生改变的时候触发
 *  params1:function
 *  params2:Object
 *     {
 *      delay:延迟执行时间
 *      onError:function
 *      }
 * */
autorun(() => {
  // count1 改变执行，count1改变不执行
  //console.log("autorun==>count1变了:", store.count1);
}, {
  delay: 200 // 两秒后再执行
})
/**
 * when(fun,fun)
 * 两个callback,当第一个callback返回一个布尔值才决定是否执行第二个callback
 * 注意:只执行一此，即使编码时when()在autorun()，还是先执行了when,再执行autorun
*/
when(() => {
  return store.count1 > 0
}, () => {
  console.log("cout1大于0,执行了when函数");
})
/**
 * reaction(
 * callbalk1(){return  statu},
 * callbalk2(statu,reaction){if(statu = 99) reaction.dispose();}
 * )
 * 两个callback，第二个callback接受第一个callback的状态，和reaction对象，可以根据status
 * 判断是否停止对statu的监听（dispose后，status改变将不监听数据变化）
 *  但是:我又报错了 ==>'Reaction[Reaction@4]' TypeError: reaction.dispose is not a function
 *  应该是版本的问题
*/
reaction(
  () => { return store.count1 },
  (data, reaction) => {
    console.log('reaction', data);
    if (data > 9999) {
      reaction.dispose();
    }
  }
)
@inject("store")
@observer
class Explain extends React.Component {
  constructor() {
    super()
    this.state = {

    }
  }
  // 就算属性sum2
  @computed get sumClass() {
    return this.props.store.count1 + this.props.store.count2
  }
  // 在这里的定义的autorun()只执行一次，不会随着状态的改变发生变化
  @autorun(() => {
    // console.log("autorunInClass", store.count1);
  })
  /**
   * 在Class里面用action装饰的函数里面修改状态，也可以监听到状态改变
   * 但是警告：严格模式下是不可以的
   * [MobX] Since strict-mode is enabled, 
   * changing (observed) observable values without using an action is not allowed. 
   * 在Class里面用没有action装饰的函数里面修改状态，也警告，也可以监听到状态改变
   * 
  */
  @action  // 奇怪：这里加了action 但是还有警告,在class里加action没有作用？
  incrementClass() {
    this.props.store.count1 = this.props.store.count1 + 99
    this.props.store.count2 = this.props.store.count2 + 99
  }

  /**
   * runInAction()
   * 为了不创建一个action函数而改变状态
   * 可以调用runInAction方法
  */
  // runInAction()里面直接修改状态是不会警告的
  changeInClass() {
    runInAction(() => {
      this.props.store.count1 = this.props.store.count1 + 99
      this.props.store.count2 = this.props.store.count2 + 99
    })
  }


  /**
   * 调用Store里的ReturnRunInaction
   * 
   * 
   * */
  HandleStoRunInacton() {
    console.log('HandleStoRunInacton');
    let result = this.props.store.returnRunInaction()
    // .then(res => {
    //   console.log('====', res);
    // })
    console.log('HandleStoRunInacton', result);

  }




  //每次数据的改变都会触发render 函数,所有不要再render里面写业务代码
  render() {
    const { store } = this.props
    return (
      <div >
        <div>
          <p>count1=<span>{store.count1}</span></p>
          <p>count2=<span>{store.count2}</span></p>
          <hr />
          <p>sumInStore=<span>{store.sumStore}</span></p>
          <p>sumInClass=<span>{this.sumClass}</span></p>
        </div>
        <div>
          <div>
            <h2>store里定义的mobx方法</h2>
            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { store.increment() }}>cout ++</Button>
            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { store.incrementcount2() }}>count2 ++</Button>
            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { store.resetCout() }}>重置</Button>
            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { store.asyncChange() }}>异步方法</Button>

          </div>

          <div>
            <h2>Class里定义的mobx方法</h2>
            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { this.incrementClass() }}>class里的action</Button>
            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { this.HandleStoRunInacton() }}>ReturnRunInAction</Button>

            <Button style={{ marginRight: '5px' }} type='primary' onClick={() => { this.changeInClass() }}>runInAction</Button>
          </div>
        </div>
      </div>
    )
  }
}

function renderNode() {
  return (<Explain store={new Store()} />)
}

export default renderNode;